package slxy.jiangtao.dts.core.common.service.impl;

import com.alibaba.fastjson.JSON;
import slxy.jiangtao.dts.core.common.bean.Constants;
import slxy.jiangtao.dts.core.common.bean.FormCriteria;
import slxy.jiangtao.dts.core.common.bean.FormQuery;
import slxy.jiangtao.dts.core.common.dao.ICommonDao;
import slxy.jiangtao.dts.core.common.hibernate.qbc.CriteriaQuery;
import slxy.jiangtao.dts.core.common.hibernate.qbc.Page;
import slxy.jiangtao.dts.core.common.service.CommonService;
import slxy.jiangtao.dts.core.util.EntityUtil;
import slxy.jiangtao.dts.core.util.StringUtil;
import org.apache.commons.lang.StringEscapeUtils;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.*;

@Service("commonService")
@Transactional
public class CommonServiceImpl implements CommonService {

    private static final Logger logger = LoggerFactory.getLogger(CommonServiceImpl.class);

    @Autowired
    public ICommonDao commonDao;

    @Override
    public <T> void evict(T entity) {
        commonDao.evict(entity);
    }

    /**
     * 保存实体
     *
     * @param entity
     * @param <T>
     * @return
     */
    public <T> Serializable save(T entity) {
        return commonDao.save(entity);
    }

    @Override
    public <T> Serializable save(T entity, Runnable runnable) {
        Serializable savedFlag = commonDao.save(entity);
        runnable.run();
        return savedFlag;
    }

    /**
     * 保存或更新实体
     *
     * @param entity
     * @param <T>
     */
    public <T> void saveOrUpdate(T entity) {
        commonDao.saveOrUpdate(entity);
    }

    /**
     * 删除实体
     *
     * @param entity
     * @param <T>
     */
    public <T> void delete(T entity) {
        commonDao.delete(entity);
    }

    /**
     * 删除实体集合
     *
     * @param <T>
     * @param entities
     */
    public <T> void deleteAllEntitie(Collection<T> entities) {
        commonDao.deleteAllEntitie(entities);
    }


    /**
     * 根据实体名获取对象
     *
     * @param class1
     * @param id
     * @param <T>
     * @return
     */
    public <T> T get(Class<T> class1, Serializable id) {
        return commonDao.get(class1, id);
    }

    /**
     * 根据实体名返回全部对象
     *
     * @param clas
     * @param <T>
     * @return
     */
    public <T> List<T> getList(Class clas) {
        return commonDao.loadAll(clas);
    }

    /**
     * 根据实体名获取对象
     *
     * @param entityName
     * @param id
     * @param <T>
     * @return
     */
    public <T> T getEntity(Class entityName, Serializable id) {
        return commonDao.getEntity(entityName, id);
    }

    /**
     * 根据实体名称和字段名称和字段值获取唯一记录
     *
     * @param <T>
     * @param entityClass
     * @param propertyName
     * @param value
     * @return
     */
    public <T> T findUniqueByProperty(Class<T> entityClass, String propertyName, Object value) {
        return commonDao.findUniqueByProperty(entityClass, propertyName, value);
    }

    /**
     * 按属性查找对象列表
     *
     * @param entityClass
     * @param propertyName
     * @param value
     * @param <T>
     * @return
     */
    public <T> List<T> findByProperty(Class<T> entityClass, String propertyName, Object value) {
        return commonDao.findByProperty(entityClass, propertyName, value);
    }

    /**
     * 加载全部实体
     *
     * @param <T>
     * @param entityClass
     * @return
     */
    public <T> List<T> loadAll(final Class<T> entityClass) {
        return commonDao.loadAll(entityClass);
    }

    /**
     * 获取单个实体
     *
     * @param hql
     * @param <T>
     * @return
     */
    public <T> T singleResult(String hql) {
        return commonDao.singleResult(hql);
    }

    /**
     * 删除实体主键ID删除对象
     *
     * @param entityName
     * @param id
     * @param <T>
     */
    public <T> void deleteEntityById(Class entityName, Serializable id) {
        commonDao.deleteEntityById(entityName, id);
    }

    /**
     * 更新指定的实体
     *
     * @param <T>
     * @param pojo
     */
    public <T> void updateEntity(T pojo) {
        commonDao.updateEntitie(pojo);
    }

    /**
     * 通过hql 查询语句查找对象
     *
     * @param <T>
     * @param hql
     * @return
     */
    public <T> List<T> findByQueryString(String hql) {
        return commonDao.findByQueryString(hql);
    }

    /**
     * 根据sql更新
     *
     * @param sql
     * @return
     */
    public int updateBySqlString(String sql) {
        return commonDao.updateBySqlString(sql);
    }

    /**
     * 根据sql查找List
     *
     * @param <T>
     * @param query
     * @return
     */
    public <T> List<T> findListbySql(String query) {
        return commonDao.findListbySql(query);
    }

    /**
     * 通过属性称获取实体带排序
     *
     * @param entityClass
     * @param propertyName
     * @param value
     * @param isAsc
     * @param <T>
     * @return
     */
    public <T> List<T> findByPropertyisOrder(
            Class<T> entityClass, String propertyName, Object value, boolean isAsc) {
        return commonDao.findByPropertyisOrder(entityClass, propertyName, value, isAsc);
    }

    /**
     * 获取Session
     *
     * @return
     */
    public Session getSession() {
        return commonDao.getSession();
    }

    /**
     * @param entityName
     * @param exampleEntity
     * @return
     */
    public List findByExample(final String entityName, final Object exampleEntity) {
        return commonDao.findByExample(entityName, exampleEntity);
    }

    /**
     * 通过cq获取全部实体
     *
     * @param <T>
     * @param cq
     * @return
     */
    public <T> List<T> getListByCriteriaQuery(final CriteriaQuery cq, Boolean ispage) {
        return commonDao.getListByCriteriaQuery(cq, ispage);
    }

    /**
     * 根据CriteriaQuery获取列表
     *
     * @param cq
     * @param <T>
     * @return
     */
    @Override
    public <T> List<T> getListByCriteriaQuery(CriteriaQuery cq) {
        return getListByCriteriaQuery(cq, false);
    }

    /**
     * 执行SQL
     *
     * @param sql
     * @param param
     * @return
     */
    public Integer executeSql(String sql, List<Object> param) {
        return commonDao.executeSql(sql, param);
    }

    /**
     * 执行SQL
     *
     * @param sql
     * @param param
     * @return
     */
    public Integer executeSql(String sql, Object... param) {
        return commonDao.executeSql(sql, param);
    }

    /**
     * 执行SQL
     *
     * @param sql
     * @param param
     * @return
     */
    public Integer executeSql(String sql, Map<String, Object> param) {
        return commonDao.executeSql(sql, param);
    }

    /**
     * 执行SQL
     *
     * @param sql
     * @param param
     * @return
     */
    public Object executeSqlReturnKey(String sql, Map<String, Object> param) {
        return commonDao.executeSqlReturnKey(sql, param);
    }

    /**
     * 执行SQL
     *
     * @param sql
     * @return
     */
    public Long getCountForJdbc(String sql) {
        return commonDao.getCountForJdbc(sql);
    }

    /**
     * 执行SQL
     *
     * @param sql
     * @param objs
     * @return
     */
    public Long getCountForJdbcParam(String sql, Object[] objs) {
        return commonDao.getCountForJdbcParam(sql, objs);
    }

    /**
     * 批量保存
     *
     * @param entities
     * @param <T>
     */
    public <T> void batchSave(List<T> entities) {
        this.commonDao.batchSave(entities);
    }

    @Override
    public <T> void batchSave(List<T> entities, Runnable callback) {
        this.batchSave(entities);
        callback.run();
    }

    /**
     * 通过hql 查询语句查找对象
     *
     * @param <T>
     * @param hql
     * @return
     */
    public <T> List<T> findHql(String hql, Object... param) {
        return this.commonDao.findHql(hql, param);
    }

    /**
     * 通过DetachedCriteria查找实体
     *
     * @param dc
     * @param <T>
     * @return
     */
    public <T> List<T> findByDetached(DetachedCriteria dc) {
        return this.commonDao.findByDetached(dc);
    }

    /**
     * 调用存储过程
     *
     * @param procedureSql
     * @param params
     * @param <T>
     * @return
     */
    public <T> List<T> executeProcedure(String procedureSql, Object... params) {
        return this.commonDao.executeProcedure(procedureSql, params);
    }

    /**
     * 获取分页
     *
     * @param dc
     * @param firstResult
     * @param maxResult
     * @param <T>
     * @return
     */
    public <T> List<T> pageList(DetachedCriteria dc, int firstResult, int maxResult) {
        return this.commonDao.pageList(dc, firstResult, maxResult);
    }

    /**
     * 分页查询
     *
     * @param cq
     * @param <T>
     * @return
     */
    @Override
    public <T> Page getPage(CriteriaQuery cq) {
        return this.commonDao.getPageList(cq, true);
    }

    @Override
    public <T> Page getPage(CriteriaQuery cq, Boolean needPage) {
        return this.commonDao.getPageList(cq, needPage);
    }

    @Override
    public Page getPage(FormQuery formQuery, Class entityClass) {
        return this.getPage(formQuery, entityClass, true);
    }

    @Override
    public Page getPage(FormQuery formQuery) {
        String entity = formQuery.getEntity();
        if (StringUtil.isEmpty(entity)) {
            throw new RuntimeException("实体名不能为空");
        }
        Class<?> entityClazz = null;
        try {
            entityClazz = Class.forName(Constants.BASE_ENTITY_PACKAGE + entity);
        } catch (ClassNotFoundException e) {
            logger.error("实体转换异常 " + e.getMessage());
            e.printStackTrace();
        }
        if (entityClazz == null) {
            String error = "不存在的实体 " + entity;
            logger.error(error);
            throw new RuntimeException(error);
        }
        return getPage(formQuery, entityClazz);
    }

    /**
     * 表单分页查询
     *
     * @param formQuery
     * @param formQuery
     * @param needPage
     * @return
     */
    @Override
    public Page getPage(FormQuery formQuery, Class entityClass, Boolean needPage) {
        CriteriaQuery cq = new CriteriaQuery(entityClass);
        // 分页设置
        if (needPage) {
            cq.setCurPage(formQuery.getCurrPage());
            cq.setPageSize(formQuery.getPageSize());
        }

        // 查询条件设置
        DetachedCriteria detachedCriteria = cq.getDetachedCriteria();
        Optional.ofNullable(formQuery.getCriterias())
                .ifPresent(
                        conditions ->
                                conditions.forEach(
                                        condition -> {
                                            Optional.ofNullable(condition.getCondition())
                                                    .ifPresent(
                                                            cond -> {
                                                                String field = condition.getField();
                                                                Object value = condition.getValue();
                                                                Optional.ofNullable(value).ifPresent(val -> {
                                                                    switch (cond.trim().toUpperCase()) {
                                                                        case "EQ":
                                                                            detachedCriteria.add(Restrictions.eq(field, val));
                                                                            break;
                                                                        case "LIKE":
                                                                            detachedCriteria.add(Restrictions.like(field, val));
                                                                            break;
                                                                        case "GT":
                                                                            detachedCriteria.add(Restrictions.gt(field, val));
                                                                            break;
                                                                        case "GE":
                                                                            detachedCriteria.add(Restrictions.ge(field, val));
                                                                            break;
                                                                        case "LT":
                                                                            detachedCriteria.add(Restrictions.lt(field, val));
                                                                            break;
                                                                        case "LE":
                                                                            detachedCriteria.add(Restrictions.le(field, val));
                                                                            break;
                                                                        case "IN":
                                                                            if (val instanceof Collection) {
                                                                                detachedCriteria.add(Restrictions.in(field, (Collection) val));
                                                                            } else if (val.getClass().isArray()) {

                                                                                detachedCriteria.add(Restrictions.in(field, (Object[]) val));
                                                                            }
                                                                            break;
                                                                        default:
                                                                    }
                                                                });
                                                            });
                                        }));
        cq.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY.getClass());
        // 排序规则设置
        Optional.ofNullable(formQuery.getSorts()).ifPresent(cq::setOrder);
        return getPage(cq, needPage);
    }

    @Override
    public Page getPageByNativeSQL(Class entityClass, int currPage, int pageSize, String where) {
        if (currPage <= 0) {
            currPage = Constants.DEFAULT_CURRENT_PAGE;
        }
        if (pageSize <= 0) {
            pageSize = Constants.DEFAULT_PAGE_SIZE;
        }
        String tableName = EntityUtil.getTableName(entityClass);
        Map<String, String> columns = EntityUtil.getColumnNames(entityClass);
        StringBuilder sqlBuilder = new StringBuilder(" from " + tableName);
        if (StringUtil.isNotEmpty(where)) {
            sqlBuilder.append(" where " + where);
        }
        List<String> columnAndProperties = new ArrayList<>();
        List<String> propertities = new ArrayList<>();
        columns
                .entrySet()
                .forEach(
                        entry -> {
                            String value = entry.getValue();
                            propertities.add(value);
                            columnAndProperties.add(entry.getKey() + " as " + value);
                        });
        Session session = this.getSession();
        List countList = session.createNativeQuery("select count(1) " + sqlBuilder.toString()).list();
        List dbData =
                session
                        .createNativeQuery(
                                "select "
                                        + String.join(",", columnAndProperties)
                                        + sqlBuilder.append(" limit " + ((currPage - 1) * pageSize) + ", " + pageSize))
                        .list();
        List<Map<String, Object>> returnList = new ArrayList<>();
        Optional.ofNullable(dbData)
                .ifPresent(
                        data ->
                                data.forEach(
                                        entities -> {
                                            Optional.ofNullable(entities)
                                                    .ifPresent(
                                                            obj -> {
                                                                Object[] entity = (Object[]) obj;
                                                                HashMap<String, Object> map = new HashMap<>();
                                                                for (int i = 0; i < entity.length; i++) {
                                                                    map.put(propertities.get(i), entity[i]);
                                                                }
                                                                returnList.add(map);
                                                            });
                                        }));
        return new Page(returnList, pageSize, currPage, Integer.valueOf(countList.get(0).toString()));
    }

    @Override
    public Page getPageByNativeSQL(Class entityClass, int currPage, int pageSize, List<FormCriteria> formCriterias, Object delFlag) {
        StringBuilder sqlBuilder = new StringBuilder("");
        if (formCriterias != null && formCriterias.size() != 0) {
            sqlBuilder.append(" 1=1 ");
            for (FormCriteria criteria : formCriterias) {
                if (criteria.getValue() != null && !criteria.getValue().toString().isEmpty() && !criteria.getValue().toString().replaceAll("%", "").trim().isEmpty()) {
                    String filed = StringEscapeUtils.escapeSql(criteria.getField());
                    String value = StringEscapeUtils.escapeSql(criteria.getValue().toString());
                    String condition = StringEscapeUtils.escapeSql(criteria.getCondition());
                    sqlBuilder.append(" and " + filed + " " + condition + " '" + value + "'");
                }
            }
            if (delFlag != null) {
                sqlBuilder.append(" and del_flag = " + delFlag.toString());
            }
        }
        return getPageByNativeSQL(entityClass, currPage, pageSize, sqlBuilder.toString());
    }


    /**
     * 根据sql查找List，结果为List<Map>
     *
     * @param <T>
     * @param query
     * @return
     */
    public <T> List<T> findListMapBySql(String query) {
        return commonDao.findListMapBySql(query);
    }

    /**
     * 通用列表值查询
     */
    @Override
    public String getDictCommon(List<Map<String, String>> list) {
        try {
            Map<String, List> res = new HashMap<>();
            String va = "";
            for (int i = 0; i < list.size(); i++) {
                Map<String, String> kv = list.get(i);
                List resList = getMetaKeyValueList(kv);
                System.out.println(JSON.toJSONString(resList));
                res.put(kv.get("dictName") == null ? kv.get("entity") : kv.get("dictName"), resList);
                va += "dict." + (kv.get("dictName") == null ? kv.get("entity") : kv.get("dictName")) + "=" + JSON.toJSONString(resList) + ";";
            }
            return va;
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * 根据一个Map集合里的实体名称信息查询一个实体的所有信息
     */
    @Override
    public List getMetaKeyValueList(Map<String, String> kv) {
        String entity = kv.get("entity");
        if (StringUtil.isEmpty(entity)) {
            throw new RuntimeException("实体名不能为空");
        }
        Class<?> entityClazz = null;
        try {
            entityClazz = Class.forName(Constants.BASE_ENTITY_PACKAGE + kv.get(entity));
        } catch (ClassNotFoundException e) {
            logger.error("实体转换异常 " + e.getMessage());
            e.printStackTrace();
        }
        if (entityClazz == null) {
            String error = "不存在的实体 " + entity;
            logger.error(error);
            throw new RuntimeException(error);
        }
        List list = getList(entityClazz);
        return list == null ? null : list;
    }
}
